#include "gObjects.h"

treeItem::treeItem(const QList<QVariant> &data, treeItem *parent){

	parentItem = parent;
	itemData = data;

}

void treeItem::setData(const QVariant &variant, int column){

	itemData[column] = variant; 

}

treeItem::~treeItem(){

	qDeleteAll(childItems);
	
}

void treeItem::appendChild(treeItem *child){

	childItems.append(child);

}

treeItem *treeItem::child(int row){

	return childItems.value(row);

}

int treeItem::childCount() const{

	return childItems.count();

}

int treeItem::row() const{

	if(parentItem)
		return parentItem->childItems.indexOf(const_cast<treeItem *>(this));
	return 0;

}

int treeItem::columnCount() const{

	return itemData.count();

}

QVariant treeItem::data(int column) const{

	return itemData.value(column);

}

treeItem *treeItem::parent(){

	return parentItem;

}

containerModel::containerModel(gObjectContainer *cont, QObject *parent):
	QAbstractItemModel(parent), container(cont){

	QList<QVariant> rootData;
	rootData << "Popis" << "Hodnota";
	rootItem = new treeItem(rootData);
	setupModelData(container, rootItem);

}

containerModel::~containerModel(){

	delete rootItem;

}

bool containerModel::uniqueName(std::string &name) const{
	
	int i;
	
	for(i = 0; i < tableItem->childCount(); i++){
		QVariant variant = tableItem->child(i)->child(0)->data(1);
		if(qvariant_cast<QString>(variant) == name.c_str())
			return false;
	}
	return true;
	
}

void containerModel::setupModelData(gObjectContainer *container, treeItem *item){
	
	QList<QVariant> variant;
	QList<QVariant> relative;
	gObject *go;
	treeItem *objectItem;
	gObjectContainer::iterator iter = container->begin();
	int i = 1;
	
	variant << TABLE_VALUE;
	tableItem = new treeItem(variant, item);
	activeItem = tableItem;
	item->appendChild(tableItem);
	for(; iter != container->end(); iter++){
		variant = QList<QVariant>();
		go = *iter;
		variant << OBJECT_VALUE << i;
		objectItem = new treeItem(variant, tableItem);
		tableItem->appendChild(objectItem);
		variant = QList<QVariant>();
		variant << NAME_VALUE << go->getName().c_str();
		objectItem->appendChild(new treeItem(variant, objectItem));
		variant = QList<QVariant>();
		variant << FEED_VALUE << go->getFeed();
		objectItem->appendChild(new treeItem(variant, objectItem));
		variant = QList<QVariant>();
		variant << SPINDLE_VALUE << go->getSpindle();
		objectItem->appendChild(new treeItem(variant, objectItem));
		variant = QList<QVariant>();
		variant << FORCE_RELATIVE << go->isForceRelative();
		objectItem->appendChild(new treeItem(variant, objectItem));
		i++;
	}
	
} 

QModelIndex containerModel::searchObjectName(QModelIndex index, QString &name) const{

	int i = 0;

	if(index.isValid()){
		treeItem *currentItem = static_cast<treeItem *>(index.internalPointer());
		treeItem *parentItem = currentItem->parent();
		
		if(qvariant_cast<QString>(parentItem->data(0)) == OBJECT_VALUE)
			i = parentItem->row() + 1;
		else if(qvariant_cast<QString>(currentItem->data(0)) == OBJECT_VALUE)
			i = index.row() + 1;
	}
	for(; i < tableItem->childCount(); i++){
		if(qvariant_cast<QString>(tableItem->child(i)->child(0)->data(1))
				.indexOf(name, 0, Qt::CaseInsensitive) > -1){
				
			return createIndex(i, 0, tableItem->child(i));
		}
	}
	
	return QModelIndex();

}

int containerModel::columnCount(const QModelIndex &parent) const{

	if(parent.isValid())
		return static_cast<treeItem *>(parent.internalPointer())->columnCount();
	else
		return rootItem->columnCount();

}

QVariant containerModel::data(const QModelIndex &index, int role) const{
	
	treeItem *item = static_cast<treeItem *>(index.internalPointer());
	
	if(!index.isValid())
		return QVariant();
	
	if(role == Qt::BackgroundRole && item == activeItem)
		return QColor(0xff, 0xa2, 0x00);
	if(item->parent() == tableItem){
		if(role == Qt::ForegroundRole)
			return QColor(Qt::white);
		if(role == Qt::BackgroundRole)
			return QColor(Qt::gray);
		if(role == Qt::FontRole)
			return QFont(QFont("Cronyx-Helvetica", 10, QFont::Bold));
		if(role == Qt::DisplayRole && index.column() == 1)
			return QVariant();
	}
	if(qvariant_cast<QString>(item->parent()->data(0)) == OBJECT_VALUE && 
		role == Qt::BackgroundRole){
		if(item->row() % 2 == 0)
			return QColor(0xd5, 0xd4, 0xff);
		return QColor(Qt::white);
	}
	if(role != Qt::DisplayRole){		
		return QVariant();
	}
	else if(index.column() == 1 &&
		qvariant_cast<QString>(item->data(0)) == FORCE_RELATIVE){
			
		if(qvariant_cast<bool>(item->data(1)))
			return tr("true");
		else
			return tr("false");
	}
	return item->data(index.column());
	
}

QVariant containerModel::headerData(
	int section, 
	Qt::Orientation orientation, 
	int role) const{
		
	if(orientation == Qt::Horizontal && role == Qt::DisplayRole)
		return rootItem->data(section);
	return QVariant();
		
}

QModelIndex containerModel::index(int row, int column, const QModelIndex &parent) const{

	treeItem *parentItem;

	if (!parent.isValid())
		parentItem = rootItem;
	else
		parentItem = static_cast<treeItem*>(parent.internalPointer());
	treeItem *childItem = parentItem->child(row);
	if (childItem)
		return createIndex(row, column, childItem);
	else
		return QModelIndex();
		
}

QModelIndex containerModel::parent(const QModelIndex &index) const{
	
	if (!index.isValid())
		return QModelIndex();

	treeItem *childItem = static_cast<treeItem*>(index.internalPointer());
	treeItem *parentItem = childItem->parent();
	if (parentItem == rootItem)
		return QModelIndex();
	return createIndex(parentItem->row(), 0, parentItem);

}

Qt::ItemFlags containerModel::flags(const QModelIndex &index) const{
	
	treeItem *item = static_cast<treeItem *>(index.internalPointer());
	
	if (!index.isValid())
		return Qt::ItemIsEnabled | Qt::ItemIsSelectable;
	if(qvariant_cast<QString>(item->parent()->data(0)) == OBJECT_VALUE && 
		index.column() == 1)
		return QAbstractItemModel::flags(index) | Qt::ItemIsEditable | Qt::ItemIsEnabled | Qt::ItemIsSelectable;
	return Qt::ItemIsEnabled | Qt::ItemIsSelectable;

}


int containerModel::rowCount(const QModelIndex &parent) const{
        
	treeItem *parentItem;

	if (!parent.isValid())
		parentItem = rootItem;
	else
		parentItem = static_cast<treeItem*>(parent.internalPointer());
	return parentItem->childCount();
    
}

bool containerModel::setData(const QModelIndex &index, const QVariant &value, int role){

	treeItem *item = static_cast<treeItem *>(index.internalPointer());
	gObject *object;
	std::string str;
	float buff;

	if(index.isValid() && role == Qt::EditRole){
		object = container->at(qvariant_cast<int>(item->parent()->data(1)) - 1);
		if(qvariant_cast<QString>(item->data(0)) == FEED_VALUE){
			buff = qvariant_cast<float>(value);
			if(buff != qvariant_cast<float>(item->data(1))){
				std::cout << buff << std::endl;
				object->setFeed(buff);
				emit changed(true);
			}
			else
				return false;
		}
		else if(qvariant_cast<QString>(item->data(0)) == SPINDLE_VALUE){
			buff = qvariant_cast<int>(value);
			if(buff != qvariant_cast<int>(item->data(1))){
				std::cout << buff << std::endl;
				object->setSpindle(buff);
				emit changed(true);
			}
			else
				return false;
		}
		else if(qvariant_cast<QString>(item->data(0)) == NAME_VALUE){
			str = qvariant_cast<QString>(value).toStdString();
			if(!str.size() || !uniqueName(str))
				return false;
			object->setName(str);
			emit changed(true);
		}
		else if(qvariant_cast<QString>(item->data(0)) == FORCE_RELATIVE){
			object->setForceRelative(qvariant_cast<bool>(value));
			emit changed(true);
		}
		else
			return false;
		item->setData(value, 1);
		emit dataChanged(index, index);
		return true;
	}
	return false;

}

void containerModel::setActiveGObject(gObject *object){

	int i = -1;
	
	if(object)
		i = container->getIndexByObject(object);
	if(i > -1)
		activeItem = tableItem->child(i);
	else
		activeItem = tableItem;
	emit dataChanged(QModelIndex(), QModelIndex());

}

void containerModel::setActiveItem(treeItem *item){

	activeItem = item;
	emit dataChanged(QModelIndex(), QModelIndex());

}

gObjectContainer *containerModel::getContainer(){

	return container;

}

gObjects::gObjects(QWidget *par): QWidget(par){
	
	QWidget *widget = new QWidget(); 
	
	vbox = new QVBoxLayout;
	hbox = new QHBoxLayout;
	view = new QTreeView;
	delegate = new doubleSpinBoxDelegate;
	view->setItemDelegate(delegate);
    view->setModel(0);
	helpAssistant = qobject_cast<bakalarka *>(parent())->getAssistant();
	vbox->setContentsMargins(0, 0, 0, 0);
	hbox->setContentsMargins(0, 0, 0, 5);
	view->setAllColumnsShowFocus(true);
	setLayout(vbox);
	createSearch();
	vbox->addWidget(view);
	vbox->addWidget(widget);
	widget->setLayout(hbox);
	createActions();
	createHandlers();
	
}

void gObjects::mouseDoubleClickEvent(QMouseEvent *event){
	
	QModelIndex index(view->currentIndex());
	
	if(index.isValid() && 0){
		treeItem *item = static_cast<treeItem *>(index.internalPointer());
		
		if(qvariant_cast<QString>(item->data(0)) == OBJECT_VALUE){
			containerModel *model = dynamic_cast<containerModel *>(view->model());
			
			if(model)
				model->setActiveItem(item);
		}
			
	}
	
}

void gObjects::createActions(){

	focus = new QAction(tr("Zamerit objekt"), this);
	focus->setIcon(QIcon(":img/focus.png"));
	connect(focus, SIGNAL(triggered()), this, SLOT(drawAreaFocus()));
	mark = new QAction(tr("Oznacit objekt"), this);
	mark->setIcon(QIcon(":img/mark.png"));
	connect(mark, SIGNAL(triggered()), this, SLOT(drawAreaMark()));
	help = new QAction(tr("Zobrazit nápovědu"), this);
	help->setIcon(QIcon(":img/help-sub.png"));
	connect(help, SIGNAL(triggered()), this, SLOT(showHelp()));

}

void gObjects::createSearch(){

	QHBoxLayout *hLayout = new QHBoxLayout;
	QPushButton *searchButton = new QPushButton(QIcon(":img/mark.png"), tr("Hledat"));
	
	searchLine = new QLineEdit;
	vbox->addLayout(hLayout);
	hLayout->addWidget(searchLine, 1);
	hLayout->addWidget(searchButton, 0);
	connect(searchButton, SIGNAL(clicked()), this, SLOT(searchObject()));

}

void gObjects::searchObject(){

	QString name(searchLine->text());
	QModelIndex index(view->currentIndex());
	containerModel *model = dynamic_cast<containerModel *>(view->model());

	if(model){
		index = model->searchObjectName(index, name);
		if(index.isValid())
			view->setCurrentIndex(index);
		else{
			QMessageBox box(this);
			
			box.setStandardButtons(QMessageBox::Yes | QMessageBox::No);
			box.setIcon(QMessageBox::Question);
			box.setText(tr("Požadovaný výraz nebyl nenalezen. Přejete si hledat ještě jednou od začátku ?"));
			if(box.exec() == QMessageBox::Yes){
				view->setCurrentIndex(QModelIndex());
				searchObject();
			}
		}
	}

}

void gObjects::showHelp(){

	helpAssistant->showKeyword("objects");

}

void gObjects::createHandlers(){
	
	QToolButton *focusButton = new QToolButton;
	QToolButton *markButton = new QToolButton;
	QToolButton *helpButton = new QToolButton;
	
	focusButton->setDefaultAction(focus);
	markButton->setDefaultAction(mark);
	helpButton->setDefaultAction(help);
	hbox->addWidget(new QWidget(), 1);
	hbox->addWidget(helpButton, 0, Qt::AlignRight);
	hbox->addWidget(focusButton, 0, Qt::AlignRight);
	hbox->addWidget(markButton, 0, Qt::AlignRight);

}

void gObjects::setModel(containerModel *model){

    if(model){
        const bakalarka *mainWindow = qobject_cast<const bakalarka *>(getTopWidget());
        drawArea *area = mainWindow->getDrawArea();

        view->setModel(model);
        view->expandAll();
        view->resizeColumnToContents(0);
        view->resizeColumnToContents(1);
        if(area)
            connect(model, SIGNAL(changed(bool)), area, SLOT(setChanged(bool)));
    }

}

containerModel *gObjects::getModel(){

    return dynamic_cast<containerModel *>(view->model());

}

gObject *gObjects::getObject(QModelIndex &index){

	treeItem *item = static_cast<treeItem *>(index.internalPointer());
	int i;
	
	if(qvariant_cast<QString>(item->data(0)) != TABLE_VALUE){
		if(qvariant_cast<QString>(item->data(0)) == OBJECT_VALUE)
			i = qvariant_cast<int>(item->data(1)) - 1;
		else
			i = qvariant_cast<int>(item->parent()->data(1)) - 1;
		return qobject_cast<containerModel *>(view->model())->getContainer()->at(i);
	}
	return 0;

}

void gObjects::drawAreaMark(){

	const bakalarka *mainWindow = qobject_cast<const bakalarka *>(getTopWidget());
	drawArea *area = mainWindow->getDrawArea();
	QModelIndex index = view->selectionModel()->currentIndex();
	
	if(area && index.isValid()){
		gObject *object;
		
		object = getObject(index);
		if(object)
			area->mark(object);
	}

}

void gObjects::drawAreaFocus(){

	const bakalarka *mainWindow = qobject_cast<const bakalarka *>(getTopWidget());
	drawArea *area = mainWindow->getDrawArea();
	QModelIndex index = view->selectionModel()->currentIndex();
	
	if(area && index.isValid()){
		gObject *object;

		object = getObject(index);
		if(object)
			area->focus(object);
	}

}

const QWidget *gObjects::getTopWidget() const{
	
	const QWidget *parent = this;
	
	while(parent->parentWidget()){
		parent = parent->parentWidget();
	}
	return parent;

}

doubleSpinBoxDelegate::doubleSpinBoxDelegate(QObject *parent): QItemDelegate(parent){}

QWidget *doubleSpinBoxDelegate::createEditor(
	QWidget *parent,
	const QStyleOptionViewItem &option,
	const QModelIndex &index
	) const{

	treeItem *item = static_cast<treeItem *>(index.internalPointer());
	QString name(qvariant_cast<QString>(item->data(0)));

	if(name == FEED_VALUE ){
		QDoubleSpinBox *spin = new QDoubleSpinBox(parent);
	
		spin->setMinimum(MINIMUM);
		spin->setMaximum(MAXIMUM);
		spin->setSingleStep(50.0);
		return spin;
	}
	else if(name == SPINDLE_VALUE){
		QSpinBox *spin = new QSpinBox(parent);
		
		spin->setMinimum(-1);
		spin->setMaximum(MAXIMUM);
		spin->setSingleStep(50.0);
		return spin;
	}
	else if(name == NAME_VALUE){
		QLineEdit *line = new QLineEdit(parent);
		
		return line;
	}
	else if(name == FORCE_RELATIVE){
		QComboBox *box = new QComboBox(parent);
		
		box->addItem(tr("false"));
		box->addItem(tr("true"));
		return box;
	}
	return 0;

}

QSize doubleSpinBoxDelegate::sizeHint(const QStyleOptionViewItem &options, const QModelIndex &index) const{

	//return QItemDelegate::sizeHint(options, index);
	return QSize(75, 18);

}

void doubleSpinBoxDelegate::setEditorData(QWidget *editor, const QModelIndex &index) const{

	treeItem *item = static_cast<treeItem *>(index.internalPointer());
	QString name(qvariant_cast<QString>(item->data(0)));
	
	if(name == FEED_VALUE){
		QDoubleSpinBox *spin = qobject_cast<QDoubleSpinBox *>(editor);
		
		spin->setValue(qvariant_cast<float>(item->data(1)));
	}
	else if(name == SPINDLE_VALUE){
		QSpinBox *spin = qobject_cast<QSpinBox *>(editor);
		
		spin->setValue(qvariant_cast<int>(item->data(1)));
	}
	else if(name == NAME_VALUE){
		QLineEdit *line = qobject_cast<QLineEdit *>(editor);
		//QString mask("n;_");
		
		//line->setInputMask(mask);
		line->setText(qvariant_cast<QString>(item->data(1)));
	}
	
}

void doubleSpinBoxDelegate::setModelData(
	QWidget *editor, 
	QAbstractItemModel *model, 
	const QModelIndex &index
	) const{
		
	treeItem *item = static_cast<treeItem *>(index.internalPointer());
	QString name(qvariant_cast<QString>(item->data(0)));
	
	if(name == FEED_VALUE){
		QDoubleSpinBox *spin = qobject_cast<QDoubleSpinBox *>(editor);
		
		model->setData(index, spin->value(), Qt::EditRole);
	}
	else if(name == SPINDLE_VALUE){
		QSpinBox *spin = qobject_cast<QSpinBox *>(editor);
		
		model->setData(index, spin->value(), Qt::EditRole);
	}
	else if(name == NAME_VALUE){
		QLineEdit *line = qobject_cast<QLineEdit *>(editor);
		
		model->setData(index, line->text(), Qt::EditRole);
	}
	else if(name == FORCE_RELATIVE){
		QComboBox *box = qobject_cast<QComboBox *>(editor);
		
		model->setData(index, box->currentIndex(), Qt::EditRole);
	}
	
}

void doubleSpinBoxDelegate::updateEditorGeometry(
	QWidget *editor, 
	const QStyleOptionViewItem &option, 
	const QModelIndex &index) 
	const{

	editor->setGeometry(option.rect);

}
